Spurious wakeups 란?
스레드가 알 수 없는 이유로 wait()
혹은 유사한 블로킹 연산에서 예기치 않게 깨어나는 현상이다.
즉, notify()
, notifyAll()
또는 스레드 인터럽트 등의 알려진 시그널을 받지 않았음에도 스레드가 깨어나는 것이다.
운영체제와 하드웨어 구현의 복잡성에서 기인하는 문제 중에 하나이다. 성능 최적화나 race condition 의 해결과 관련해서 발생할 수 있다.
발생하는 상황
- 성능 최적화
일부 운영체제가 커널 레벨에서 스레드, 프로세스의 상태 변경과 관련된 연산을 최적화하기 위해 배치 처리나 그룹화를 수행할 수 있다. 이런 최적화 과정 중에 스레드가 wait()
상태에서 깨어날 수 있는 내부적인 signal 이 다른 이벤트로 인해 의도치 않게 전달되어 발생할 수 있다.
예시
여러 스레드가 거의 동시에 wait()
상태로 전환될 수 있다. 이 경우, 커널은 전환이 일어나는 시기를 일정 간격으로 모아서 한꺼번에 처리하는 배치 처리를 수행할 수 있다. 이런 과정에서 예기치 않은 시점에 스레드 상태가 변경되며 발생할 수 있다.
- 경쟁 상황의 해결
운영체제에서 다양한 리소스에 대한 접근을 조정하려할 때, 여러 스레드가 프로세스 간의 경쟁 상황을 해결하는 메커니즘을 사용할 수 있다. 이때의 부작용으로 스레드가 예기치 않게 깨어날 수 있다.
예시 스레드 A, B 가 동일한 파일을 동시에 수정하려고 시도할 때, 운영체제는 한 스레드에만 접근 권한을 주고 다른 스레드는 잠시 대기 상태로 만들 수 있다. 그런데 이때 특정 스레드가 리소스에 대한 접근 권한을 받았지만, 다양한 이유로 실제로 접근하기 전에 다시 BLOCK 상태로 들어갈 수 있다. 이 경우 다른 스레드가 해당 리소스에 접근할 기회를 얻게 되면 race condition 해결을 위해 BLOCKED / WAITING 상태의 스레드를 깨울 수 있다.
데드락을 다항 시간에 해결하는 방법이 없다. 스레드가 사용하는 자원이 천차만별이라 컴퓨터가 때로는 휴리스틱한 방법을 사용한다. 바로 랜덤한 스레드를 깨우거나 죽이는 것이다...
- 하드웨어 인터럽트
하드웨어 인터럽트란 CPU, 메모리, I/O 장치 등의 하드웨어에서 발생하는 예외 상황이나 요청이다.
예시
- 인터럽트 처리: 디스크에서 데이터를 읽는 도중 오류가 발생하면 운영체제가 인터럽트를 받게 된다. 해당 인터럽트를 처리하기 위해 관련 스레드를 깨울 수 있다.
- 리소스 할당: CPU 나 메모리 같은 자원을 다른 스레드에 할당하기 위해 현재 실행 중인 스레드를 중단시킬 수 있다. 반대로 스레드가 요청한 자원이 사용 가능해질 경우 해당 스레드를 깨워 작업을 계속하도록 할 수 있다.
- 시그널 또는 예외 처리: 특정 스레드가 시그널, 예외를 받았을 때, 운영체제가 해당 스레드를 깨워 해당 시그널이나 예외를 처리하게 할 수 있다.